package com.search;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;

import net.paoding.analysis.analyzer.PaodingAnalyzer;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.SimpleAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.highlight.Formatter;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.Scorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.jdbc.JdbcDirectory;
import org.apache.lucene.store.jdbc.dialect.MySQLDialect;
import org.apache.lucene.util.Version;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.ContextLoader;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.dao.ArticleDAO;
import com.dao.QuestionDAO;
import com.model.Article;
import com.model.Question;
import com.model.Tag;
import com.util.SearchRecord;

@Service
public class SearchService {
	// 索引文件位置
	//File dataFile = new File(System.getProperty("webapp.root")+"WEB-INF/lucene");
	private Directory jdbcDirectory = new MyJDBCDirectory(JDBCDatabaseUtil.getDataSource(), new MySQLDialect(), "LUCENE_INDEX_TABLE");;
	// 使用庖丁分词器
	Analyzer analyzer = new PaodingAnalyzer();
	
	@Autowired
	private ArticleDAO articleDAO;

	@Autowired
	private QuestionDAO questionDAO;

	/**
	 * 建立新的答案索引数据
	 */
	public SearchService() {
		Analyzer analyzer = new SimpleAnalyzer(Version.LUCENE_36);
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LUCENE_36, analyzer);
        IndexWriter indexWriter = null;
        try {
            indexWriter = new IndexWriter(getJdbcDirectory(), indexWriterConfig);
        } catch (CorruptIndexException e) {
            e.printStackTrace();
        } catch (LockObtainFailedException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (indexWriter != null) {
                try {
                    indexWriter.close();
                } catch (CorruptIndexException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    indexWriter = null;
                }
            }
        }

	}

	@Transactional
	public void bulidQuestionIndex(Question question) {
		Directory directory = null;
		IndexWriter writer = null;
		try {
			directory = getJdbcDirectory();
			IndexWriterConfig indexWriterConfig = new IndexWriterConfig(
					Version.LUCENE_36, analyzer);
			writer = new IndexWriter(getJdbcDirectory(), indexWriterConfig);

			StringBuffer tags = new StringBuffer();
			List<Tag> taglist = questionDAO.findTagsByQuestionId(question
					.getId());
			for (Tag tag : taglist) {
				tags.append(tag.getName());
				tags.append(" ");
			}

			
			Integer id = question.getId();
			String type="question";
			String tagstr = tags.toString();
			String title = question.getQuestion();
			String content = question.getAnswer();
			
			Document doc = new Document();

			/**
			 * Field.Index.NO 表示不索引 Field.Index.ANALYZED 表示分词且索引
			 * Field.Index.NOT_ANALYZED 表示不分词且索引
			 */
			doc.add(new Field("id", type + "#" + String.valueOf(id),
					Field.Store.YES, Field.Index.NOT_ANALYZED));
			doc.add(new Field("tags", tagstr, Field.Store.YES,
					Field.Index.ANALYZED));
			doc.add(new Field("title", title, Field.Store.YES,
					Field.Index.ANALYZED));
			doc.add(new Field("content", content, Field.Store.YES,
					Field.Index.ANALYZED));

			writer.addDocument(doc);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			try {
				writer.optimize();
				writer.close();
				directory.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 建立新的文章索引数据
	 */
	@Transactional
	public void bulidArticleIndex(Article article) {
		Directory directory = null;
		IndexWriter writer = null;
		try {

			directory = getJdbcDirectory();
			IndexWriterConfig indexWriterConfig = new IndexWriterConfig(
					Version.LUCENE_36, analyzer);
			writer = new IndexWriter(getJdbcDirectory(), indexWriterConfig);
			
			StringBuffer tags = new StringBuffer();
			List<Tag> taglist = articleDAO.findTagsByArticleId(article.getId());
			for (Tag tag : taglist) {
				tags.append(tag.getName());
				tags.append(" ");
			}

			Integer id = article.getId();
			String type="article";
			String tagstr = tags.toString();
			String title = article.getTitle();
			String content = article.getContent();
			
			Document doc = new Document();

			/**
			 * Field.Index.NO 表示不索引 Field.Index.ANALYZED 表示分词且索引
			 * Field.Index.NOT_ANALYZED 表示不分词且索引
			 */
			doc.add(new Field("id", type + "#" + String.valueOf(id),
					Field.Store.YES, Field.Index.NOT_ANALYZED));

			doc.add(new Field("tags", tagstr, Field.Store.YES,
					Field.Index.ANALYZED));
			doc.add(new Field("title", title, Field.Store.YES,
					Field.Index.ANALYZED));
			doc.add(new Field("content", content, Field.Store.YES,
					Field.Index.ANALYZED));

			writer.addDocument(doc);

		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			try {
				writer.optimize();
				writer.close();
				directory.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	@Transactional
	public void updateArticleIndex(Article article) {
		StringBuffer tags = new StringBuffer();
		List<Tag> taglist = articleDAO.findTagsByArticleId(article.getId());
		for (Tag tag : taglist) {
			tags.append(tag.getName());
			tags.append(" ");
		}

		update(article.getId(), "article", tags.toString(), article.getTitle(),
				article.getContent());
	}
	
	/**
	 * 更新索引
	 */
	@Transactional
	public void update(Integer id, String type, String tags, String title,
			String content) {

		try {
			Directory directory = getJdbcDirectory();

			IndexWriterConfig indexWriterConfig = new IndexWriterConfig(
					Version.LUCENE_36, analyzer);
			IndexWriter writer = new IndexWriter(directory, indexWriterConfig);

			Document doc = new Document();
			doc.add(new Field("id", type + "#" + String.valueOf(id),
					Field.Store.YES, Field.Index.NOT_ANALYZED));

			doc.add(new Field("tags", tags, Field.Store.YES,
					Field.Index.ANALYZED));
			doc.add(new Field("title", title, Field.Store.YES,
					Field.Index.ANALYZED));
			doc.add(new Field("content", content, Field.Store.YES,
					Field.Index.ANALYZED));

			Term term = new Term("id", type + "#" + String.valueOf(id));
			writer.deleteDocuments(term);
			writer.updateDocument(term, doc);
			writer.close();
			directory.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
//
//	/**
//	 * 添加到document中
//	 */
//	@Transactional
//	public void addIndexDocument(IndexWriter writer, Integer id, String type,
//			String tags, String title, String content) {
//		try {
//			Document doc = new Document();
//
//			/**
//			 * Field.Index.NO 表示不索引 Field.Index.ANALYZED 表示分词且索引
//			 * Field.Index.NOT_ANALYZED 表示不分词且索引
//			 */
//			doc.add(new Field("id", type + "#" + String.valueOf(id),
//					Field.Store.YES, Field.Index.NOT_ANALYZED));
//
//			doc.add(new Field("tags", type, Field.Store.YES,
//					Field.Index.ANALYZED));
//			doc.add(new Field("title", content, Field.Store.YES,
//					Field.Index.ANALYZED));
//			doc.add(new Field("content", content, Field.Store.YES,
//					Field.Index.ANALYZED));
//
//			writer.addDocument(doc);
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//	}
//
//	@Transactional
//	public void updateArticleIndex(Article article) {
//		StringBuffer tags = new StringBuffer();
//		List<Tag> taglist = articleDAO.findTagsByArticleId(article.getId());
//		for (Tag tag : taglist) {
//			tags.append(tag.getName());
//			tags.append(" ");
//		}
//
//		update(article.getId(), "article", tags.toString(), article.getTitle(),
//				article.getContent());
//	}

	@Transactional
	public void updateQuestionIndex(Question question) {
		StringBuffer tags = new StringBuffer();
		List<Tag> taglist = questionDAO.findTagsByQuestionId(question.getId());
		for (Tag tag : taglist) {
			tags.append(tag.getName());
			tags.append(" ");
		}
		
		update(question.getId(), "question", tags.toString(),
				question.getQuestion(), question.getAnswer());
	}



	/**
	 * 删除索引
	 */
	@Transactional
	public void delete(Integer id, String type) {
		try {
			Directory directory = getJdbcDirectory();

			IndexWriterConfig indexWriterConfig = new IndexWriterConfig(
					Version.LUCENE_36, analyzer);
			IndexWriter writer = new IndexWriter(directory, indexWriterConfig);

			Term term = new Term("id", type + "#" + String.valueOf(id));

			writer.deleteDocuments(term);
			writer.close();
			directory.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
//	@Transactional
//	public List searchFromTag(String value){
//		IndexSearcher indexSearcher = null;
//		String columnName = "tags";
//		List result = new ArrayList();
//        try {
//            /**
//             * Specify the version
//             */
//            Analyzer analyzer = new SimpleAnalyzer(Version.LUCENE_36);
//            /**
//             * Create query columnname (index name passed), we built out index for name, author and publisher so
//             * we have to search against the same.
//             */
//            Query query = new QueryParser(Version.LUCENE_36, columnName, analyzer).parse(value);
//            IndexReader indexReader = IndexReader.open(this.getJdbcDirectory());
//            indexSearcher = new IndexSearcher(indexReader);
//            /**
//             * This will hold all the results which results from the search
//             * operation
//             */
//            TopDocs topDocs = indexSearcher.search(query, 15);
//            if (topDocs.scoreDocs.length > 0) {
//                System.out.println("Found :  Book with id = " + indexSearcher.doc(topDocs.scoreDocs[0].doc).get("id") + " , tags = "
//                        + indexSearcher.doc(topDocs.scoreDocs[0].doc).get("tags") + " ,title = "
//                        + indexSearcher.doc(topDocs.scoreDocs[0].doc).get("title") + " ,Publisher = "
//                        + indexSearcher.doc(topDocs.scoreDocs[0].doc).get("content") + " with hits : " + topDocs.scoreDocs[0].doc);
//            } else {
//                System.out.println("No Record found");
//            }
//            
//            ScoreDoc[] hits = topDocs.scoreDocs;
//            
//			for (ScoreDoc scoreDoc : hits) {
//				Document hitDoc = indexSearcher.doc(scoreDoc.doc);
//
//				String id = hitDoc.get("id");
//				String title = hitDoc.get("title");
//				String[] idarr = id.split("#");
//				int idint = Integer.parseInt(idarr[1]);
//				SearchRecord sr = new SearchRecord();
//				sr.setId(idint);
//				sr.setTitle(title);
//				sr.setType(idarr[0]);
//				result.add(sr);
//			}
//            
//        } catch (ParseException e) {
//            e.printStackTrace();
//        } catch (CorruptIndexException e) {
//            e.printStackTrace();
//        } catch (IOException e) {
//            e.printStackTrace();
//        } finally {
//            if (indexSearcher != null) {
//                try {
//                    indexSearcher.close();
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            }
//            indexSearcher = null;
//        }
//        return result;
//	}

	/**
	 * 搜索
	 * 
	 * @param keyword
	 */
	@Transactional
	public void search(String keyword) {
		IndexSearcher isearcher = null;
		Directory directory = null;
		try {
			directory = this.getJdbcDirectory();

			/**
			 * 创建索引搜索器 且只读
			 */
			isearcher = new IndexSearcher(directory, true);
			
			BooleanClause.Occur[] clauses = { BooleanClause.Occur.SHOULD,
					BooleanClause.Occur.SHOULD,BooleanClause.Occur.SHOULD };
			Query query = MultiFieldQueryParser.parse(Version.LUCENE_36,
					keyword, new String[] { "tags", "title", "content"  },
					clauses, analyzer);
			

			/**
			 * 执行搜索，获取查询结果集对象
			 */
			TopDocs topDocs = isearcher.search(query, 1000);

			ScoreDoc[] hits = topDocs.scoreDocs;

			/**
			 * 关键字 高亮
			 */
			Formatter formatter = new SimpleHTMLFormatter("<font color='red'>",
					"</font>");
			Scorer scorer = new QueryScorer(query);
			Highlighter highlighter = new Highlighter(formatter, scorer);

			/**
			 * 设置摘取字符的长度，默认为100个字符
			 */
			Fragmenter fragmenter = new SimpleFragmenter(100);
			highlighter.setTextFragmenter(fragmenter);

			for (ScoreDoc scoreDoc : hits) {
				Document hitDoc = isearcher.doc(scoreDoc.doc);

				String id = hitDoc.get("id");
				String content = highlighter.getBestFragment(analyzer,
						"content", hitDoc.get("content"));

				System.out.println("id:" + id + "\n\rcontent:" + content);
			}

		} catch (Exception e) {
			throw new RuntimeException(e);
		} finally {
			try {
				isearcher.close();
				directory.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Transactional
	public List searchFromTag(String tagStr) {
		IndexSearcher isearcher = null;
		Directory directory = null;
		List result = new ArrayList();
		try {

			directory = this.getJdbcDirectory();

			/**
			 * 创建索引搜索器 且只读
			 */

			isearcher = new IndexSearcher(directory, true);

			BooleanClause.Occur[] clauses = { BooleanClause.Occur.SHOULD,
					BooleanClause.Occur.SHOULD };
			Query query = MultiFieldQueryParser.parse(Version.LUCENE_36,
					tagStr, new String[] { "tags", "title"  },
					clauses, analyzer);

			Filter filter = null;

			TopDocs topDocs = isearcher.search(query, filter, 11);

			ScoreDoc[] hits = topDocs.scoreDocs;

			for (ScoreDoc scoreDoc : hits) {
				Document hitDoc = isearcher.doc(scoreDoc.doc);

				String id = hitDoc.get("id");
				String title = hitDoc.get("title");
				String[] idarr = id.split("#");
				int idint = Integer.parseInt(idarr[1]);
				SearchRecord sr = new SearchRecord();
				sr.setId(idint);
				sr.setTitle(title);
				sr.setType(idarr[0]);
				result.add(sr);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				isearcher.close();
				directory.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	@Transactional
	public List searchFromTag(List<Tag> taglist) throws ParseException {

		StringBuffer tags = new StringBuffer();
		for (Tag tag : taglist) {
			tags.append(tag.getName());
			tags.append(" ");
		}
		return searchFromTag(tags.toString());
	}
	
	
	public void setJdbcDirectory(Directory jdbcDirectory) {
        this.jdbcDirectory = jdbcDirectory;
    }
	
	/**
     * Gets the jdbc directory.
     * 
     * @return the jdbc directory
     */
    public Directory getJdbcDirectory() {
        if (jdbcDirectory == null) {
            throw new IllegalStateException("Index not yet build, rerun indexing");
        }
        return jdbcDirectory;
    }
    /**
     * Creates the index table.
     */
    private void createIndexTable() {
        if (this.jdbcDirectory == null) {
            setJdbcDirectory(new MyJDBCDirectory(JDBCDatabaseUtil.getDataSource(), new MySQLDialect(), "LUCENE_INDEX_TABLE"));
        }
        try {
            /**
             * No need to manually create index table, create method will
             * automatically create it.
             */
            ((JdbcDirectory) getJdbcDirectory()).create();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
